-
Notifications
You must be signed in to change notification settings - Fork 26k
[hoo] Invoke subgraph + effect #167231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: gh/angelayi/132/base
Are you sure you want to change the base?
[hoo] Invoke subgraph + effect #167231
Conversation
[ghstack-poisoned]
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/167231
Note: Links to docs will display an error until the docs builds have been completed. ✅ You can merge normally! (1 Unrelated Failure)As of commit 91eb786 with merge base 460c7e1 ( UNSTABLE - The following job is marked as unstable, possibly due to flakiness on trunk:
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
cc ezyang EikanWang jgong5 wenzhe-nrv [ghstack-poisoned]
cc ezyang EikanWang jgong5 wenzhe-nrv [ghstack-poisoned]
This PR adds support for effectful ops within invoke_subgraphs.
* Most of the logic is in `invoke_subgraph.py_functionalize_impl`.
* In the functionalization metadata collection phase, we note the tokens before going further down the dispatcher, and then note the tokens after coming back from the dispatcher. If there are nodes in the invoke_subgraph subgraph that contain effects, the number of effects should change, or the tokens used for an effect should.
* We will store this effect difference in the `InvokeSubgraphCache` where the key is the identifier and value is the effect. For now we only support one effect within a subgraph.
* During the tracing part of AOTAutograd, we will then wrap the subgraph to take in and output a token.
Before:
```
def forward(self, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', x)
return invoke_subgraph
def repeated_subgraph(self, x):
record_memory = torch.ops.mylib.record_memory.default("forward", "N")
add = torch.ops.aten.add(x, x)
return add
```
After:
```
def forward(self, token, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', token, x)
getitem = invoke_subgraph[0] # output token
getitem_1 = invoke_subgraph[1]
return (getitem, getitem_1)
def repeated_subgraph(self, token, x):
with_effects = torch.ops.higher_order.with_effects(token, torch.ops.mylib.record_memory.default, 'forward', 'N')
getitem = with_effects[0] # output token
add = torch.ops.aten.add(x, x)
return (getitem, add)
```
* Then there is a bunch of logic within `_remove_effect_tokens` to handle removing the effects from the invoke_subgraph subgraph
cc ezyang EikanWang jgong5 wenzhe-nrv
[ghstack-poisoned]
This PR adds support for effectful ops within invoke_subgraphs.
* Most of the logic is in `invoke_subgraph.py_functionalize_impl`.
* In the functionalization metadata collection phase, we note the tokens before going further down the dispatcher, and then note the tokens after coming back from the dispatcher. If there are nodes in the invoke_subgraph subgraph that contain effects, the number of effects should change, or the tokens used for an effect should.
* We will store this effect difference in the `InvokeSubgraphCache` where the key is the identifier and value is the effect. For now we only support one effect within a subgraph.
* During the tracing part of AOTAutograd, we will then wrap the subgraph to take in and output a token.
Before:
```
def forward(self, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', x)
return invoke_subgraph
def repeated_subgraph(self, x):
record_memory = torch.ops.mylib.record_memory.default("forward", "N")
add = torch.ops.aten.add(x, x)
return add
```
After:
```
def forward(self, token, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', token, x)
getitem = invoke_subgraph[0] # output token
getitem_1 = invoke_subgraph[1]
return (getitem, getitem_1)
def repeated_subgraph(self, token, x):
with_effects = torch.ops.higher_order.with_effects(token, torch.ops.mylib.record_memory.default, 'forward', 'N')
getitem = with_effects[0] # output token
add = torch.ops.aten.add(x, x)
return (getitem, add)
```
* Then there is a bunch of logic within `_remove_effect_tokens` to handle removing the effects from the invoke_subgraph subgraph
cc ezyang EikanWang jgong5 wenzhe-nrv
[ghstack-poisoned]
| assert all( | ||
| isinstance(o, (torch.Tensor, int, torch.SymInt, torch.Generator)) | ||
| for o in operands | ||
| if o is not None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when do you see None as input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The effect tokens are passed in as None here since we will eventually just discard these inputs.
This PR adds support for effectful ops within invoke_subgraphs.
* Most of the logic is in `invoke_subgraph.py_functionalize_impl`.
* In the functionalization metadata collection phase, we note the tokens before going further down the dispatcher, and then note the tokens after coming back from the dispatcher. If there are nodes in the invoke_subgraph subgraph that contain effects, the number of effects should change, or the tokens used for an effect should.
* We will store this effect difference in the `InvokeSubgraphCache` where the key is the identifier and value is the effect. For now we only support one effect within a subgraph.
* During the tracing part of AOTAutograd, we will then wrap the subgraph to take in and output a token.
Before:
```
def forward(self, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', x)
return invoke_subgraph
def repeated_subgraph(self, x):
record_memory = torch.ops.mylib.record_memory.default("forward", "N")
add = torch.ops.aten.add(x, x)
return add
```
After:
```
def forward(self, token, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', token, x)
getitem = invoke_subgraph[0] # output token
getitem_1 = invoke_subgraph[1]
return (getitem, getitem_1)
def repeated_subgraph(self, token, x):
with_effects = torch.ops.higher_order.with_effects(token, torch.ops.mylib.record_memory.default, 'forward', 'N')
getitem = with_effects[0] # output token
add = torch.ops.aten.add(x, x)
return (getitem, add)
```
* Then there is a bunch of logic within `_remove_effect_tokens` to handle removing the effects from the invoke_subgraph subgraph
cc ezyang EikanWang jgong5 wenzhe-nrv
[ghstack-poisoned]
ghstack-source-id: 4742f7e Pull Request resolved: pytorch/pytorch#167231
This PR adds support for effectful ops within invoke_subgraphs.
* Most of the logic is in `invoke_subgraph.py_functionalize_impl`.
* In the functionalization metadata collection phase, we note the tokens before going further down the dispatcher, and then note the tokens after coming back from the dispatcher. If there are nodes in the invoke_subgraph subgraph that contain effects, the number of effects should change, or the tokens used for an effect should.
* We will store this effect difference in the `InvokeSubgraphCache` where the key is the identifier and value is the effect. For now we only support one effect within a subgraph.
* During the tracing part of AOTAutograd, we will then wrap the subgraph to take in and output a token.
Before:
```
def forward(self, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', x)
return invoke_subgraph
def repeated_subgraph(self, x):
record_memory = torch.ops.mylib.record_memory.default("forward", "N")
add = torch.ops.aten.add(x, x)
return add
```
After:
```
def forward(self, token, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', token, x)
getitem = invoke_subgraph[0] # output token
getitem_1 = invoke_subgraph[1]
return (getitem, getitem_1)
def repeated_subgraph(self, token, x):
with_effects = torch.ops.higher_order.with_effects(token, torch.ops.mylib.record_memory.default, 'forward', 'N')
getitem = with_effects[0] # output token
add = torch.ops.aten.add(x, x)
return (getitem, add)
```
* Then there is a bunch of logic within `_remove_effect_tokens` to handle removing the effects from the invoke_subgraph subgraph
cc ezyang EikanWang jgong5 wenzhe-nrv
[ghstack-poisoned]
This PR adds support for effectful ops within invoke_subgraphs.
* Most of the logic is in `invoke_subgraph.py_functionalize_impl`.
* In the functionalization metadata collection phase, we note the tokens before going further down the dispatcher, and then note the tokens after coming back from the dispatcher. If there are nodes in the invoke_subgraph subgraph that contain effects, the number of effects should change, or the tokens used for an effect should.
* We will store this effect difference in the `InvokeSubgraphCache` where the key is the identifier and value is the effect. For now we only support one effect within a subgraph.
* During the tracing part of AOTAutograd, we will then wrap the subgraph to take in and output a token.
Before:
```
def forward(self, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', x)
return invoke_subgraph
def repeated_subgraph(self, x):
record_memory = torch.ops.mylib.record_memory.default("forward", "N")
add = torch.ops.aten.add(x, x)
return add
```
After:
```
def forward(self, token, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', token, x)
getitem = invoke_subgraph[0] # output token
getitem_1 = invoke_subgraph[1]
return (getitem, getitem_1)
def repeated_subgraph(self, token, x):
with_effects = torch.ops.higher_order.with_effects(token, torch.ops.mylib.record_memory.default, 'forward', 'N')
getitem = with_effects[0] # output token
add = torch.ops.aten.add(x, x)
return (getitem, add)
```
* Then there is a bunch of logic within `_remove_effect_tokens` to handle removing the effects from the invoke_subgraph subgraph
cc ezyang EikanWang jgong5 wenzhe-nrv
[ghstack-poisoned]
This PR adds support for effectful ops within invoke_subgraphs.
* Most of the logic is in `invoke_subgraph.py_functionalize_impl`.
* In the functionalization metadata collection phase, we note the tokens before going further down the dispatcher, and then note the tokens after coming back from the dispatcher. If there are nodes in the invoke_subgraph subgraph that contain effects, the number of effects should change, or the tokens used for an effect should.
* We will store this effect difference in the `InvokeSubgraphCache` where the key is the identifier and value is the effect. For now we only support one effect within a subgraph.
* During the tracing part of AOTAutograd, we will then wrap the subgraph to take in and output a token.
Before:
```
def forward(self, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', x)
return invoke_subgraph
def repeated_subgraph(self, x):
record_memory = torch.ops.mylib.record_memory.default("forward", "N")
add = torch.ops.aten.add(x, x)
return add
```
After:
```
def forward(self, token, x):
repeated_subgraph0 = self.repeated_subgraph0
invoke_subgraph = torch.ops.higher_order.invoke_subgraph(repeated_subgraph0, 'subgraph_0', token, x)
getitem = invoke_subgraph[0] # output token
getitem_1 = invoke_subgraph[1]
return (getitem, getitem_1)
def repeated_subgraph(self, token, x):
with_effects = torch.ops.higher_order.with_effects(token, torch.ops.mylib.record_memory.default, 'forward', 'N')
getitem = with_effects[0] # output token
add = torch.ops.aten.add(x, x)
return (getitem, add)
```
* Then there is a bunch of logic within `_remove_effect_tokens` to handle removing the effects from the invoke_subgraph subgraph
cc ezyang EikanWang jgong5 wenzhe-nrv
[ghstack-poisoned]
|
Starting merge as part of PR stack under #167245 |
|
@angelayi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
|
Starting merge as part of PR stack under #167245 |
1 similar comment
|
Starting merge as part of PR stack under #167245 |
…67245) In the [previous PR](https://github.com/pytorch/pytorch/pull/167231/files#diff-e2b74af5d8b538a7d07d18507d27010703742ddad5f819992b55f5abc6d9a502R964-R966) we found that the autograd eager impl of invoke_subgraph calls the subgraph twice. If the subgraph contains effects then effects will be run twice, which is bad. This PR fixes the issue by getting the output metadata from `subgraph`'s `node.meta` if it exists. Differential Revision: [D87392740](https://our.internmc.facebook.com/intern/diff/D87392740) Pull Request resolved: #167245 Approved by: https://github.com/anijain2305 ghstack dependencies: #167231
|
@pytorchbot revert -m "break internal tests, synced with author regarding this: example error: AttributeError: 'list' object has no attribute 'dtype'" -c ghfirst |
|
@pytorchbot successfully started a revert job. Check the current status here. |
Reverting PR 167231 failedReason: Command Details for Dev Infra teamRaised by workflow job |
This reverts commit f49833d.
|
@pytorchbot revert -m "the diff breaks tests internally " -c ghfirst |
|
@pytorchbot successfully started a revert job. Check the current status here. |
This reverts commit f49833d. Reverted #167231 on behalf of https://github.com/yangw-dev due to the diff breaks tests internally ([comment](#167231 (comment)))
|
@angelayi your PR has been successfully reverted. |
This PR adds support for effectful ops within invoke_subgraphs.
invoke_subgraph.py_functionalize_impl.InvokeSubgraphCachewhere the key is the identifier and value is the effect. For now we only support one effect within a subgraph.Before:
After:
_remove_effect_tokensto handle removing the effects from the invoke_subgraph subgraphStack from ghstack (oldest at bottom):
cc @ezyang @EikanWang @jgong5 @wenzhe-nrv
Differential Revision: D87392741